﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using Emgu.CV;
using Emgu.CV.Structure;
using Emgu.Util;
using System.Diagnostics;

namespace ImageDetection
{
    public delegate void UpdateImage(Bitmap bm);
    public delegate Image GetImageFromCamera();
    public class CameraDetect
    {
        //private Capture image;        //takes images from camera as image frames
        private bool captureInProgress; // checks if capture is


        public event UpdateImage OnUpdate;
        public event GetImageFromCamera GetCameraImage;

        public CameraDetect()
        {
            captureInProgress = false;
        }

       /* private void ProcessFrame(object sender, EventArgs arg)
        {
            Image<Bgr, Byte> img = image.QueryFrame();  //line 1
            if (OnUpdate != null)
            {
                OnUpdate(img);
            }
        }
        //*/
        private void ProcessFrame(object sender, EventArgs arg)
        {
            Image myImg = null;
            if (GetCameraImage != null)
            {
                myImg = GetCameraImage.Invoke();
            }
            if (myImg != null)
            {
                Image<Bgr, Byte> ImageFrame = new Image<Bgr, Byte>(new Bitmap(myImg));//image.QueryFrame();  //line 1
                Image<Bgr, Byte> img = ImageFrame.Resize(400, 400, Emgu.CV.CvEnum.INTER.CV_INTER_LINEAR, true);
                Image<Gray, Byte> gray = img.Convert<Gray, Byte>().PyrDown().PyrUp();

                #region circle detection video
                Stopwatch watch = Stopwatch.StartNew();
                Bgr cannyThreshold = new Bgr(200, 200, 200);
                Gray cannyThresholdgray = new Gray(180);
                Gray cannyThresholdLinking = new Gray(120);
                Bgr circleAccumulatorThreshold = new Bgr(200, 200, 200);
                Gray circleAccumulatorThresholdgray = new Gray(500);
                CircleF[] circles = img.HoughCircles(
                    cannyThreshold,
                    circleAccumulatorThreshold,
                   3.0, //Resolution of the accumulator used to detect centers of the circles
                    20.0, //min distance 
                    5, //min radius
                    70 //max radius
                    )[0]; //Get the circles from the first channel

                watch.Stop();

                #endregion

                #region Canny and edge detection
                watch.Reset(); watch.Start();

                Image<Gray, Byte> cannyEdges = gray.Canny(cannyThresholdgray, cannyThresholdLinking);
                LineSegment2D[] lines = cannyEdges.HoughLinesBinary(
                    1, //Distance resolution in pixel-related units
                    Math.PI / 45.0, //Angle resolution measured in radians.
                    20, //threshold
                    30, //min Line width
                    10 //gap between lines
                    )[0]; //Get the lines from the first channel
                watch.Stop();

                #endregion

                #region Find  rectangles video
                watch.Reset(); watch.Start();

                List<MCvBox2D> boxList = new List<MCvBox2D>(); //a box is a rotated rectangle
                using (MemStorage storage = new MemStorage()) //allocate storage for contour approximation
                    for (
                       Contour<Point> contours = cannyEdges.FindContours(
                          Emgu.CV.CvEnum.CHAIN_APPROX_METHOD.CV_CHAIN_APPROX_SIMPLE,
                          Emgu.CV.CvEnum.RETR_TYPE.CV_RETR_LIST,
                          storage);
                       contours != null;
                       contours = contours.HNext)
                    {
                        Contour<Point> currentContour = contours.ApproxPoly(contours.Perimeter * 0.157, storage);

                        if (currentContour.Area > 250) //only consider contours with area greater than 250
                        {

                            if (currentContour.Total == 4) //The contour has 4 vertices.
                            {
                                #region determine if all the angles in the contour are within [80, 100] degree
                                bool isRectangle = true;
                                Point[] pts = currentContour.ToArray();
                                LineSegment2D[] edges = PointCollection.PolyLine(pts, true);

                                for (int i = 0; i < edges.Length; i++)
                                {
                                    double angle = Math.Abs(
                                       edges[(i + 1) % edges.Length].GetExteriorAngleDegree(edges[i]));
                                    if (angle < 80 || angle > 100)
                                    {
                                        isRectangle = false;
                                        break;
                                    }
                                }
                                #endregion

                                if (isRectangle) boxList.Add(currentContour.GetMinAreaRect());
                            }
                        }
                    }
                watch.Stop();
                //  msgBuilder.Append(String.Format("Triangles & Rectangles - {0} ms; ", watch.ElapsedMilliseconds));
                #endregion

                #region draw shapes video
                Image<Bgr, Byte> shapeimage = img.CopyBlank();

                foreach (MCvBox2D box in boxList)
                {
                    img.Draw(box, new Bgr(Color.DarkOrange), 2);
                }

                foreach (CircleF circle in circles)
                {
                    img.Draw(circle, new Bgr(Color.Blue), 2);
                }

                #endregion


                //                    CamImageBox.Image = img;        //line 2
                if (OnUpdate != null)
                {
                    OnUpdate((Bitmap)img.Bitmap.Clone());
                }
            }
            
        }//*/
        public void toggleImageDetect()
        {
            #region if capture is not created, create it now
            /*if (image == null)
            {
                try
                {

                    image = new Capture();

                }
                catch (NullReferenceException excpt)
                {
                    MessageBox.Show(excpt.Message);
                }
            }//*/
            #endregion

            //if (image != null)
            {
                if (captureInProgress)
                {  //if camera is getting frames then stop the capture and set button Text // "Start" for resuming capture
                    //btnStart.Text = "Start!"; //
                    Application.Idle -= ProcessFrame;
                }
                else
                {
                    //if camera is NOT getting frames then start the capture and set button // Text to "Stop" for pausing capture
                    //btnStart.Text = "Stop";
                    Application.Idle += ProcessFrame;
                }

                captureInProgress = !captureInProgress;
            }
        }
    }
}
